package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.PointsStatisticsVO;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.service.IPointsRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.tianji.common.utils.DateUtils.DATE_SUFFIX_FORMATTER;
import static com.tianji.learning.constants.RedisConstants.POINTS_BOARD_KEY_PREFIX;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author 老李头儿
 * @since 2025-03-31
 */
@Service
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {

    private final RedisTemplate redisTemplate;

    public PointsRecordServiceImpl(@Qualifier("redisTemplate") RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 添加积分记录
     *
     * @param userId        用户id
     * @param points        积分值
     * @param pointsRecordType 积分记录类型
     */
    @Override
    public void addPointsRecord(Long userId, Integer points, PointsRecordType pointsRecordType) {
        // 1、判断当前积分类型是否有上限
        int maxPoints = pointsRecordType.getMaxPoints();   // 20
        LocalDateTime now = LocalDateTime.now();
        if (maxPoints > 0) {
            // 2、如果有，则判断当天该用户以获取积分总额 是否超过每日上限
            // 2.1、查询【当前用户】【今日】通过该【积分类型】已获取的【积分总数】
            int currentDayPoints = queryTodayTotalPointsByType(userId, pointsRecordType, now);  // 10

            // 2.2、判断是否超过每日上限
            if(currentDayPoints >= maxPoints){
                return;
            }

            // 2.3、处理 已有积分 + 本次待加积分 > 积分上限的情况，如果超过，不能直接加，而是要加上：上限 - 已有积分的 差值部分
            if(currentDayPoints + points > maxPoints){
                points = maxPoints - currentDayPoints;
            }
        }

        // 3、如果没有，直接保存积分记录
        PointsRecord pointsRecord = new PointsRecord().setPoints(points).setUserId(userId).setType(pointsRecordType);
        save(pointsRecord);
        // 4、累加用户积分到redis Zset结构
        //4.1组装key (board:202503)
        String key = POINTS_BOARD_KEY_PREFIX + now.format(DATE_SUFFIX_FORMATTER);

        //4.2累加积分
        redisTemplate.opsForZSet().incrementScore(key, userId, points);


    }

    /**
    * @Description 查询当前用户今日积分情况
    * @Author 老李头儿
    * @Date 2025年04月01 16:58:18
    * @param
    * @Return java.util.List<com.tianji.learning.domain.vo.PointsStatisticsVO>
    **/
    @Override
    public List<PointsStatisticsVO> queryTodayMyPoints() {
        // 1、查询统计当前用户当天获取积分情况（分组统计）
        // select `type`,sum(points) as points from points_record where create_time between '2025-03-31 00:00:00'
        // and '2025-03-31 23:59:59' and user_id = 2 group by `type`
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime begin = DateUtils.getDayStartTime(now);
        LocalDateTime end = DateUtils.getDayEndTime(now);

        List<PointsRecord> list = this.query()
                .select("`type`", "sum(points) as points")
                .eq("user_id", UserContext.getUser())
                .between("create_time", begin, end)
                .groupBy("`type`")
                .list();

        // 2、循环查询结果，封装Vo集合
        List<PointsStatisticsVO> voList = new ArrayList<>(list.size());
        for (PointsRecord pointsRecord : list) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setType(pointsRecord.getType().getDesc());
            vo.setMaxPoints(pointsRecord.getType().getMaxPoints());
            vo.setPoints(pointsRecord.getPoints());
            voList.add(vo);
        }

        // 3、处理没有查询结果的类型
        Set<String> descSet = voList.stream().map(vo -> vo.getType()).collect(Collectors.toSet());
        Set<PointsRecordType> types = Arrays.stream(PointsRecordType.values()).filter(p -> !descSet.contains(p.getDesc())).collect(Collectors.toSet());
        for (PointsRecordType type : types) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setType(type.getDesc());
            vo.setMaxPoints(type.getMaxPoints());
            vo.setPoints(0);
            voList.add(vo);
        }
        return voList;
    }

    /**
    * @Description 查询当天该用户以获取积分总额
    * @Description
    * @Author 老李头儿
    * @Date 2025年04月01 16:53:50
    * @param userId
    * @param pointsRecordType
    * @param now
    * @Return int
    **/

    private int queryTodayTotalPointsByType(Long userId, PointsRecordType pointsRecordType, LocalDateTime now) {
        // select sum(points) from points_record where user_id = ? and type = ? and created_time between ? and ?
        LocalDateTime begin = DateUtils.getDayStartTime(now);
        LocalDateTime end = DateUtils.getDayEndTime(now);
        PointsRecord record = this.query()
                .select("sum(points) as points")
                .eq("user_id", userId)
                .eq("type", pointsRecordType)
                .between("create_time", begin, end)
                .one();
        return ObjectUtils.isEmpty(record) || ObjectUtils.isEmpty(record.getPoints()) ? 0 : record.getPoints();
    }
}
